package com.jeasonzhao.commons.parser.lex;

import com.jeasonzhao.commons.basic.StringCollection;
import com.jeasonzhao.commons.utils.Algorithms;

public class SqlTokenizer extends BaseTokenizer
{
    public SqlTokenizer()
    {
        super(KnownConfiguration.SQL92.getInstance());
    }

    public SqlTokenizer(String str)
    {
        super(KnownConfiguration.SQL92.getInstance(),str);
    }

    public LexToken nextToken()
        throws LexException
    {
        LexToken t = super.nextToken();
        if(null != t && t.isSpecial() && t.equals("?"))
        {
            char cnext = this.current();
            if(cnext != END
               && cnext != ';' //Allow the end of query
               && cnext != ',' //Allow the end of query
               && cnext != ')' //Allow the end of query
               && Character.isSpaceChar(cnext) == false)
            {
                throwException(t,"SQL Parse Error: \"?\" can not be followed by \"" + cnext + "\"");
            }
        }
        return t;
    }

    public static final String cleanSQL(String str)
    {
        try
        {
            if(str == null || str.trim().length() < 1)
            {
                return null;
            }
            else
            {
                SqlTokenizer t = new SqlTokenizer(str);
                LexTokenCollection alltks = t.getTokens(false,false);
                return alltks.toString();
            }
        }
        catch(LexException ex)
        {
            return str;
        }
    }

    public static final StringCollection splitSQLStatement(String strSql)
        throws LexException
    {
        if(Algorithms.isEmpty(strSql))
        {
            return null;
        }
        LexTokenCollectionCollection al = splitSQLToken(strSql);
        if(null == al)
        {
            return null;
        }
        StringCollection ret = new StringCollection();

        for(int n = 0;n < al.size();n++)
        {
            LexTokenCollection c = al.get(n);
            String strSql2 = c.toString().trim();
            if(strSql2.length() > 0)
            {
                ret.add(strSql2);
            }
        }
        return ret;
    }

    public static final StringCollection getSQLParametersName(String strSql)
        throws LexException
    {
        StringCollection ret = new StringCollection();
        if(Algorithms.isEmpty(strSql))
        {
            return ret;
        }
        SqlTokenizer tker = new SqlTokenizer(strSql);
        java.util.HashSet<String> set = new java.util.HashSet<String>();
        LexTokenCollection tksall = tker.getTokens(false,false);
        for(int n = 0;null != tksall && n < tksall.size();n++)
        {
            LexToken k = tksall.get(n);
            if(isSQLParameterToken(k))
            {
                if(k.isSpecial() == false && set.contains(k.getToken()))
                {
                    continue;
                }
                set.add(k.getToken());
                ret.add(k.getToken());
            }
        }
        return ret;
    }

    public static final boolean isSQLParameterToken(LexToken token)
    {
        return null != token
            && token.getToken() != null
            && token.getToken().length() > 0
            && (token.isName() || token.isSpecial())
            && "#@:?".indexOf(token.getToken().charAt(0)) >= 0;
    }

    public static final boolean isSQLParameterToken(String token)
    {
        token = null == token ? null : token.trim();
        return null != token
            && token.length() > 0
            && "#@:?".indexOf(token.charAt(0)) >= 0;
    }

    public static final String replaceParameterInSQL(String strSql,String strParameterName,Object objValue)
        throws LexException
    {
        strParameterName = strParameterName == null ? null : strParameterName.trim();
        if(null == strSql
           || strSql.trim().length() < 1
           || null == strParameterName
           || strParameterName.trim().length() < 1)
        {
            return strSql;
        }
        SqlTokenizer tk = new SqlTokenizer(strSql);
        StringBuffer buf = new StringBuffer();
        LexTokenCollection alltks = tk.getTokens(true,false);
        for(int n = 0;null != alltks && n < alltks.size();n++)
        {
            LexToken token = alltks.get(n);
            if(isSQLParameterToken(token) && token.getToken().equalsIgnoreCase(strParameterName))
            {
                buf.append(Algorithms.toString(objValue));
            }
            else
            {
                buf.append(token.getInitString());
            }
        }
        return buf.toString();
    }
    public static final boolean isSelectStatement(String sql)
        throws LexException
    {
        String t=getFirstSQLToken(sql);
        return t!=null&&t.equalsIgnoreCase("select");
    }
    public static final String getFirstSQLToken(String sql)
        throws LexException
    {
        if(Algorithms.isEmpty(sql))
        {
            return null;
        }
        else
        {
            SqlTokenizer tk = new SqlTokenizer(sql);
            LexTokenCollection ret =tk.getTokens(false,false);
            return null==ret||ret.size()<1?null:ret.get(0).getToken();
        }
    }
    public static final LexTokenCollectionCollection splitSQLToken(String strSql)
        throws LexException
    {
        if(Algorithms.isEmpty(strSql))
        {
            return null;
        }
        SqlTokenizer tk = new SqlTokenizer(strSql);
        LexTokenCollectionCollection ret = new LexTokenCollectionCollection();
        LexTokenCollection buf = new LexTokenCollection();
        LexTokenCollection alltks = tk.getTokens(true,false);
        for(int n = 0;null != alltks && n < alltks.size();n++)
        {
            LexToken token = alltks.get(n);
            if((token.isSpecial() && token.equals(";"))
               || (token.isName() && token.getToken().equalsIgnoreCase("GO")) //Sql Server Styles
                )
            {
                if(buf.size() > 0)
                {
                    ret.add(buf);
                }
                buf = new LexTokenCollection();
            }
            else
            {
                buf.add(token);
            }
        }
        if(buf.size() > 0)
        {
            ret.add(buf);
        }
        return ret.size() > 0 ? ret : null;
    }
    public static SqlTranslateResult translateToNamedParameterSQL(String sql,boolean removeParameterPrefix,Object...parameters)
        throws LexException
    {
        if(Algorithms.isEmpty(sql))
        {
            return null;
        }
         SqlTokenizer tk = new SqlTokenizer(sql);
         StringBuffer newSql=new StringBuffer();
         int nidx=0;
         java.util.Map<String,Object> map=new java.util.HashMap<String,Object>();
         for(LexToken t: tk.getTokens(false,false))
         {
             if(isSQLParameterToken(t))
             {
                 String pnameInSql=null;
                 Object value=null==parameters||nidx>=parameters.length?null:parameters[nidx];
                 if(t.getToken().equals("?"))
                 {
                     pnameInSql=":TEMP_A_PARAMETER"+nidx;
                 }
                 else
                 {
                     pnameInSql=t.getToken();
                 }
                 nidx++;
                 map.put(removeParameterPrefix?pnameInSql.substring(1):pnameInSql,value);
                 newSql.append(t.replaceToken(pnameInSql));
             }
             else
             {
                 newSql.append(t.getInitString());
             }
         }
         SqlTranslateResult ret=new SqlTranslateResult();
         ret.setNewSql(newSql.toString());
         ret.setOriginalSql(sql);
         ret.setParameters(map);
         return ret;
    }
}
